package com.balabala.gym_user.service.impl;

import com.balabala.constant.TokenConstant;
import com.balabala.constant.WxConstant;
import com.balabala.dto.UserInfoDto;
import com.balabala.dto.UserNameDto;
import com.balabala.exception.CustomerException;
import com.balabala.gym_enum.ExceptionEnum;
import com.balabala.gym_system.api.AdminFeignClient;
import com.balabala.gym_system.pojo.vo.AdminVo;
import com.balabala.gym_user.constant.SmsConstant;
import com.balabala.gym_user.constant.UserConstant;
import com.balabala.gym_user.pojo.dto.TokenDTO;
import com.balabala.gym_user.pojo.dto.UserQueryDTO;
import com.balabala.gym_user.pojo.entity.Token;
import com.balabala.gym_user.pojo.entity.User;
import com.balabala.gym_user.mapper.TokenMapper;
import com.balabala.gym_user.mapper.UserMapper;
import com.balabala.gym_user.pojo.entity.VipLevel;
import com.balabala.gym_user.pojo.vo.UserPageVO;
import com.balabala.gym_user.service.UserService;
import com.balabala.gym_user.service.UserVipCardService;
import com.balabala.gym_user.service.VipLevelService;
import com.balabala.gym_user.utils.CheckMobile;
import com.balabala.gym_user.pojo.vo.WxLoginVO;
import com.balabala.result.Result;
import com.balabala.vo.UserNameVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @Author 何涛
 * @Date: 2023/04/30/ 13:14
 * @description 用户service实现类
 */
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper,User> implements UserService {
    @Autowired
    RedisTemplate redisTemplate;
    @Autowired
    HttpServletRequest request;
    @Autowired

    TokenMapper tokenMapper;
    @Autowired

    RestTemplate restTemplate;
    @Autowired
    UserVipCardService userVipCardService;
    @Autowired
    VipLevelService vipLevelService;
    @Autowired
    AdminFeignClient adminFeignClient;

    @Override
    public User addDefaultUser(String phone) {
        // 查询默认会员等级
        Long defaultVipLevel=vipLevelService.getOne(new LambdaQueryWrapper<VipLevel>().eq(VipLevel::getDefaultUse,0L)).getId();
        User user = User.builder().createTime(new Date()).integral(1).phone(phone).vipLevelId(defaultVipLevel).status(UserConstant.ON).photo(UserConstant.DEFAULT_PHOTO).build();
        boolean save = save(user);
        if(!save){
            log.info("保存默认用户失败");
            throw new CustomerException(ExceptionEnum.SAVE_DEFAULT_USER);
        }
        log.info("用户信息：{}",user.toString());
        return user;
    }

    @Override
    public TokenDTO oneLoginBySmsCode(String phone, String code) {
        if(StringUtils.isEmpty(code)||code.length()!=SmsConstant.CODE_LENGTH) {
            log.info("用户电话:{]的验证码不是{}位",phone,SmsConstant.CODE_LENGTH);
            throw new CustomerException(ExceptionEnum.SMS_CODE_ILLEGAL);
        }
        //查询这个电话码中是否有验证码
        String redisCode = (String) redisTemplate.boundValueOps(SmsConstant.PREFIX_SMS_PHONE + phone).get();
        if(!code.equals(redisCode)) {
            log.info("验证码不正确，code：{]",code);
            throw new CustomerException(ExceptionEnum.SMS_CODE_ERROR);
        }
        log.info("登录成功。。。");
        String token= UUID.randomUUID().toString().replace("-","");
        TokenDTO tokenDto=new TokenDTO();
        log.info("查找用户是否之前登录过。。。");
        LambdaQueryWrapper<User> lambdaQueryWrapper=new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(User::getPhone,phone);
        User user = getOne(lambdaQueryWrapper);
        Token saveToken ;

        //获取用户请求头
        String userAgent = request.getHeader("User-Agent");
        //检查用户是pc端登录还是移动端登录
        if(!CheckMobile.check(userAgent)){
            log.info("用户正在使用pc端接入。。。");
            if(user==null){
                log.info("用户第一次登录，为用户创建一个初始账号");
                user=addDefaultUser(phone);
                log.info("保存用户pc端的token信息");
                saveToken = Token.builder().userId(user.getId()).pcToken(token).lastLoginIp(request.getRemoteAddr()).lastLoginTime(new Date()).createTime(new Date()).build();
                tokenMapper.insert(saveToken);
            }
            else {
                log.info("redis清除pc端token的缓存");
                saveToken = tokenMapper.selectOne(new LambdaQueryWrapper<Token>().eq(Token::getUserId, user.getId()));
                redisTemplate.boundValueOps(TokenConstant.USER_TOKEN_SECRET+saveToken.getPcToken()).expire(-1, TimeUnit.SECONDS);

                //更新用户token表中的数据
                saveToken.setPcToken(token);
                saveToken.setLastLoginIp(request.getRemoteAddr());
                saveToken.setLastLoginTime(new Date());
                tokenMapper.updateById(saveToken);
                log.info("更新用户在表中的信息完毕。。。");
            }

        }
        else{
            String refreshToken=UUID.randomUUID().toString().replace("-","");
            log.info("用户正在使用移动端登录,生成刷新refreshToken:{}",refreshToken);
            Calendar calendar=Calendar.getInstance();
            calendar.add(Calendar.DAY_OF_MONTH,TokenConstant.REFRESH_TOKEN_EXPIRE);
            Date time = calendar.getTime();
            if(user==null){
                log.info("保存app端的token到用户token表中");
                saveToken = Token.builder().userId(user.getId()).appToken(token).lastLoginIp(request.getRemoteAddr()).lastLoginTime(new Date()).createTime(new Date()).
                        refreshToken(refreshToken).refreshTokenExpire(time).build();
                tokenMapper.insert(saveToken);
            }
            else {
                log.info("清除redis移动端token的缓存");
                saveToken = tokenMapper.selectOne(new LambdaQueryWrapper<Token>().eq(Token::getUserId, user.getId()));
                redisTemplate.boundValueOps(TokenConstant.USER_TOKEN_SECRET+saveToken.getAppToken()).expire(-1, TimeUnit.SECONDS);
                //更新数据库中的数据

                saveToken.setAppToken(token);
                saveToken.setRefreshToken(refreshToken);
                saveToken.setRefreshTokenExpire(time);
                saveToken.setLastLoginIp(request.getRemoteAddr());
                saveToken.setLastLoginTime(new Date());
                tokenMapper.updateById(saveToken);
                log.info("更新用户token表中的信息完毕。。。");
                tokenDto.setRefreshToken(refreshToken);
            }
        }
        tokenDto.setToken(token);
        //保存用户信息到redis中
        saveUserInfoToRedis(user,new UserInfoDto(),token);
        return tokenDto;
    }

    @Override
    public WxLoginVO loginByWx(String code) {
        try{
            User user;
            //1.后端带着code和appId&秘钥去找微信https://api.weixin.qq.com/sns/jscode2session
            //获取openId和session_key
            String url="https://api.weixin.qq.com/sns/jscode2session?appid="+ WxConstant.APP_ID +"&secret="+WxConstant.APP_SECRET+
                    "&js_code="+code+"&grant_type=authorization_code";
            String jsonData=restTemplate.getForObject(url,String.class);
            log.info("微信返回登录的数据为：{}",jsonData);
            ObjectMapper objectMapper=new ObjectMapper();
            WxLoginVO wxLoginVo = objectMapper.readValue(jsonData, WxLoginVO.class);
            if(wxLoginVo==null||wxLoginVo.getSession_key()==null){
                log.info("微信登录失败。。。");
                throw new CustomerException(ExceptionEnum.FAIL_WX_LOGIN);
            }
            log.info("用户微信登录成功。。。");
            String token=UUID.randomUUID().toString();
            wxLoginVo.setToken(token);
            //根据openId查询该用户是不是第一次登录
            Token tokenByOpenId = tokenMapper.selectOne(new LambdaQueryWrapper<Token>().eq(Token::getOpenId, wxLoginVo.getOpenid()));
            if(tokenByOpenId==null){
                log.info("第一次使用微信授权登录,创建初始化账户");
                user = addDefaultUser(null);
                tokenByOpenId = Token.builder().wxToken(token).openId(wxLoginVo.getOpenid()).createTime(new Date())
                        .lastLoginIp(request.getRemoteAddr()).lastLoginTime(new Date())
                        .userId(user.getId()).build();
                tokenMapper.insert(tokenByOpenId);
            }
            else {
                log.info("用户不是第一次使用微信登录。。。");
                //清除redis中的用户缓存
                redisTemplate.boundValueOps(TokenConstant.USER_TOKEN_SECRET+tokenByOpenId.getWxToken()).expire(-1,TimeUnit.SECONDS);
                tokenByOpenId.setWxToken(token);
                tokenByOpenId.setLastLoginTime(new Date());
                tokenByOpenId.setLastLoginIp(request.getRemoteAddr());
                tokenMapper.updateById(tokenByOpenId);
                //查询用户信息
                user = getById(tokenByOpenId.getUserId());
            }

            //保存token到redis中
            saveUserInfoToRedis(user,new UserInfoDto(),token);
            log.info("微信->用户信息已保存到redis当中");
            return wxLoginVo;
        }catch (Exception e)
        {
            throw new CustomerException(ExceptionEnum.FAIL_WX_LOGIN);
        }

    }

    @Override
    public UserInfoDto getUserInfo(String userToken) {
        //登录续期
        redisTemplate.boundValueOps(TokenConstant.USER_TOKEN_SECRET+userToken).expire(TokenConstant.CACHE_USER_EXPIRE,TimeUnit.SECONDS);
        return (UserInfoDto) redisTemplate.boundValueOps(TokenConstant.USER_TOKEN_SECRET+userToken).get();
    }

    @Override
    public PageInfo getUserPageList(UserQueryDTO userQueryDto) {
        PageHelper.startPage(userQueryDto.getPage(), userQueryDto.getLimit());

        List<UserPageVO> userPageVOList =baseMapper.findUserPageList(userQueryDto);

        userPageVOList.forEach(userPageVO -> {

            //根据用户id查询拥有的会员卡
            List<String> userCardNameList=userVipCardService.findUserCardNameByUserId(userPageVO.getId());
            userPageVO.setUserVipCards(userCardNameList);
            // 根据教练id查询对应教练
            if(userPageVO.getInstructorId()!=null){
                Result<AdminVo> instructor = adminFeignClient.findById(userPageVO.getInstructorId());
                if(instructor.getData()!=null){
                    userPageVO.setInstructor(instructor.getData().getName());
                }
            }

        });

        PageInfo<UserPageVO> pageInfo=new PageInfo<>(userPageVOList);

        return pageInfo;
    }

    @Override
    public User findById(Long id) {
        User user = getById(id);
        user.setVipLevel(vipLevelService.getById(user.getVipLevelId()));
        return user;
    }

    @Override
    public void saveUserInfoToRedis(User user, UserInfoDto userInfoDto,String token) {
        //使用新的token保存用户信息到redis中
        //查询对应的会员等级
        VipLevel vipLevel = vipLevelService.getById(user.getVipLevelId());
        userInfoDto.setVipLevelName(vipLevel.getName());
        BeanUtils.copyProperties(user,userInfoDto);
        redisTemplate.boundValueOps(TokenConstant.USER_TOKEN_SECRET+token).set(userInfoDto);
        redisTemplate.boundValueOps(TokenConstant.USER_TOKEN_SECRET+token).expire(TokenConstant.CACHE_USER_EXPIRE,TimeUnit.SECONDS);
    }

    @Override
    public User getUserDetailInfo() {
        UserInfoDto userInfo = getUserInfo(request.getHeader("user-token"));
        log.info("获取用户详细信息,用户id:{}",userInfo.getId());
        return getById(userInfo.getId());
    }

    @Override
    public List<UserNameVo> findVidByName(UserNameDto userNameDto) {
        return getBaseMapper().findVidByName(userNameDto);
    }

}
